package br.ufrgs.dbproject.idgenerator.dewey;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import br.ufrgs.dbproject.Context;
import br.ufrgs.dbproject.idgenerator.ID;
import br.ufrgs.dbproject.idgenerator.IDGenerator;
import br.ufrgs.dbproject.idgenerator.IDGeneratorStrategy;

/**
 * ID generator based on Dewey algorithm for constructing XML index path.
 * 
 * @author diego
 * @see <a href="http://ieeexplore.ieee.org/iel5/5207611/5207612/05207699.pdf?arnumber=5207699">IEEE article</a> - Applying Dewey Encoding to Construct XML Index for Path and Keyword Query
 */
public class DeweyIDGenerator implements IDGenerator<DeweyID> {
	
	/**
	 * Parser context which holds information about the parsing process such as
	 * the generated IDs Stack.
	 */
	private final Context<DeweyID> context;
	
	/**
	 * Generators strategies used to generate Dewey IDs.
	 */
	private final List<IDGeneratorStrategy<DeweyID>> generators;
	
	/**
	 * Creates a new instance of {@link DeweyIDGenerator} with a custom generation strategies list
	 * 
	 * @param generators generation strategies
	 */
	public DeweyIDGenerator(List<IDGeneratorStrategy<DeweyID>> generators) {
		this.generators = generators;
		this.context = new Context<DeweyID>(DeweyID.createEmptyID());
	}
	
	/**
	 * Creates a new instance of {@link DeweyIDGenerator} with a custom generation strategies list
	 * and a custom {@link Context}.
	 * 
	 * @param context the ID generation context
	 * @param generators generation strategies list
	 */
	public DeweyIDGenerator(Context<DeweyID> context, List<IDGeneratorStrategy<DeweyID>> generators) {
		this.generators = generators;
		this.context = context;
	}

	/**
	 * Creates a new instance of {@link DeweyIDGenerator} with an empty {@link Context} and
	 * the default ID generation strategies (Sibling ID generation, Child ID generation and Root ID generation).
	 * 
	 * @see RootIDGenerator#generate(Context)
	 * @see ChildIDGenerator#generate(Context)
	 * @see SiblingIDGenerator#generate(Context)
	 */
	public DeweyIDGenerator() {
		
		this.context = new Context<DeweyID>(DeweyID.createEmptyID());
		this.generators = new LinkedList<IDGeneratorStrategy<DeweyID>>();
		
		generators.add(new SiblingIDGenerator());
		generators.add(new ChildIDGenerator());
		generators.add(new RootIDGenerator());
	}

	/**
	 * Generates the next ID from the current {@link Context}
	 * and pushes it into the Context {@link Stack}.
	 * 
	 * @see Context#saveID(ID)
	 */
	public DeweyID generateNext() {
		
		for (IDGeneratorStrategy<DeweyID> generator : generators) {
			if (generator.canHandle(context)) {
				DeweyID generatedID = generator.generate(context);
				context.saveID(generatedID);
				return generatedID;
			}
		}
		
		throw new RuntimeException(String.format("Could not generate next ID for stack size: %s and lastCreatedElementID: %s", context.getSize(), context.getLastSavedID()));
	}

	/**
	 * Retrieve the current ID generation {@link Context}.
	 */
	public Context<DeweyID> getContext() {
		return context;
	}
}
